package com.hzz.framework.filter;


import com.alibaba.fastjson.JSONObject;
import com.hzz.framework.Response;
import com.hzz.framework.util.*;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@WebFilter(filterName = "jwtFilter", urlPatterns = {"/boss/*","/api/*"})
@Slf4j
public class JwtFilter implements Filter {
    @Autowired
    JwtFilterProperties jwtFilterProperties;
    @Autowired
    private EhcacheUtil cache;

    @Override
    public void init(FilterConfig filterConfig) {
        log.info("=============boss==“授权签名”过滤=============");
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) {
        if (servletResponse instanceof HttpServletResponse) {
            HttpServletRequest request = ((HttpServletRequest) servletRequest);
            HttpServletResponse response = ((HttpServletResponse) servletResponse);
            String srcAuthorization = request.getHeader(CommonConstant.JWT_HEADER_TOKEN_KEY);
            try {
                // 无需登陆白名单
                boolean flag = false;
                if (jwtFilterProperties.getExcludeUrlPatterns() != null && jwtFilterProperties.getExcludeUrlPatterns().size() > 0) {
                    String url = request.getServletPath();
                    PathMatcher matcher = new AntPathMatcher();
                    for (int i = 0; i < jwtFilterProperties.getExcludeUrlPatterns().size(); i++) {
                        if (matcher.match(jwtFilterProperties.getExcludeUrlPatterns().get(i), url)) {
                            flag = true;
                            break;
                        }
                    }
                }

//                if (!flag) {
//                    // Referer校验
//                    String referer = request.getHeader("Referer");
//                    String currServerName = request.getScheme() + "://" + request.getServerName();
//                    log.debug(referer + "=======Referer==startsWith====" + currServerName);
//                    if (!(ValidatorUtil.notEmpty(referer) && referer.trim().startsWith(currServerName))) {
//                        log.error("Referer校验失败:" + (request.getScheme() + "://" + request.getServerName()));
//                        WebUtil.sendJson(response, Response.error(110, "Referer校验失败!"));
//                        return;
//                    }
//                }
//                // 头攻击检测host 添加白名单
//                String requestHost = request.getHeader("host");  //当前访问的url地址host
//                if (requestHost == null || jwtFilterProperties.getHostWhitePatterns() == null || !jwtFilterProperties.getHostWhitePatterns().contains(requestHost.toLowerCase())) {
//                    log.error("HOST头检测失败:" + (requestHost));
//                    WebUtil.sendJson(response, Response.error(403, "HOST头检测失败!"));
//                    return;
//                }

                boolean not_found_jwt_header = false;
                if (ValidatorUtil.isEmpty(srcAuthorization)) {
                    not_found_jwt_header = true;
                    srcAuthorization = request.getParameter(CommonConstant.JWT_HEADER_TOKEN_KEY);
                }
                if (!flag && ValidatorUtil.notEmpty(srcAuthorization) && !srcAuthorization.startsWith("Basic ")) {
                    String authorizationToken = null;
                    boolean jwtSflag = false;
                    if (srcAuthorization.startsWith(CommonConstant.JWT_HEADER_TOKEN_PREFIX_BOSS)) {//符合待处理token标记
                        jwtSflag = true;
                        authorizationToken = (String) cache.get(CommonConstant.JWT_CACHE_TOKEN_KEY_BOSS + srcAuthorization);
                        request.setAttribute(CommonConstant.JWT_HEADER_TOKEN_PREFIX_BOSS + CommonConstant.JWT_HEADER_TOKEN_KEY, srcAuthorization);
                    } else if (srcAuthorization.startsWith(CommonConstant.JWT_HEADER_TOKEN_PREFIX_APP)) {
                        jwtSflag = true;
                        authorizationToken = (String) cache.get(CommonConstant.JWT_CACHE_TOKEN_KEY_APP + srcAuthorization);
                        request.setAttribute(CommonConstant.JWT_HEADER_TOKEN_PREFIX_APP + CommonConstant.JWT_HEADER_TOKEN_KEY, srcAuthorization);
                    } else {
                        authorizationToken = srcAuthorization;
                    }
                    if (ValidatorUtil.isEmpty(authorizationToken)) {
                        throw new SignatureException("");
                    }
//                    servletRequest.setAttribute(CommonConstant.JWT_CACHE_TOKEN_KEY, authorizationToken);
                    final Claims claims = JwtUtil.parseJWT(authorizationToken);
                    if (chain != null) {
                        JSONObject jsonObject = JSONObject.parseObject(claims.getSubject());
                        //强制下线时间
                        String key = CommonConstant.JWT_OFFLINE + jsonObject.getString("id");
                        if (cache.get(key) != null) {
                            if (claims.getIssuedAt().getTime() < Long.parseLong("" + cache.get(key))) {
                                //cache.del(key);
                                WebUtil.sendJson(response, Response.error(110, jwtFilterProperties.getMessage().getForceOffline()));
                                return;
                            }
                        }
                        key = CommonConstant.JWT_ISSUEAT + jsonObject.getString("id");
                        String value = "" + cache.get(key);
                        if (ValidatorUtil.notEmpty(value)
                                && !value.equals("" + claims.getIssuedAt().getTime())) {//用户最新token签发时间
                            long inssueTime = Long.parseLong(value);
                            long nowMillis = System.currentTimeMillis();
                            if (nowMillis - inssueTime < 0 || nowMillis - inssueTime > 5 * 1000) {
                                if (log.isDebugEnabled()) {
                                    log.error("账号[{}]鉴权登录失败,当前TOKEN签发时间{}!=最后签发时间{}", jsonObject.getString("account"), claims.getIssuedAt().getTime(), value);
                                }
                                WebUtil.sendJson(response, Response.error(110, jwtFilterProperties.getMessage().getTokenExpire()));
                                return;
                            }
                        }
                    }
                    if (jwtSflag) {
                        chain.doFilter(new HttpHeaderRequestWrapper(not_found_jwt_header, request, authorizationToken), servletResponse);
                    } else {
                        chain.doFilter(servletRequest, servletResponse);
                    }
                } else {
                    chain.doFilter(servletRequest, servletResponse);
                }
            } catch (final SignatureException e) {
                WebUtil.sendJson(response, Response.error(403, "登录信息失效!" + e.getMessage()));
            } catch (ExpiredJwtException e) {
                WebUtil.sendJson(response, Response.error(403, "授权过期!" + e.getMessage()));
            } catch (Throwable e) {
                log.error("授权检查异常 Authorization={},Exception={}", srcAuthorization, e.getMessage());
                WebUtil.sendJson(response, Response.error(403, "授权检查异常!" + e.getMessage()));
            }
        }
    }

    /**
     * 替换授权token
     */
    private static class HttpHeaderRequestWrapper extends HttpServletRequestWrapper {
        private final Map<String, String> customHeaders;
        private final String authorizationToken;


        public HttpHeaderRequestWrapper(boolean not_found_jwt_header, HttpServletRequest request, String authorizationToken) {
            super(request);
            this.authorizationToken = authorizationToken;
            this.customHeaders = new HashMap<>();
            putHeader(CommonConstant.JWT_HEADER_TOKEN_KEY, authorizationToken);
        }

        void putHeader(String name, String value) {
            this.customHeaders.put(name, value);
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            Set<String> set = new HashSet<>(customHeaders.keySet());
            Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
            while (e.hasMoreElements()) {
                String n = e.nextElement();
                set.add(n);
            }
            return Collections.enumeration(set);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            if (null != name && name.equalsIgnoreCase(CommonConstant.JWT_HEADER_TOKEN_KEY)) {
                return new Enumeration<String>() {
                    private boolean hasGetted = false;

                    @Override
                    public String nextElement() {
                        if (hasGetted) {
                            throw new NoSuchElementException();
                        } else {
                            hasGetted = true;
                            return getRedisAuthorizationToken();
                        }
                    }

                    @Override
                    public boolean hasMoreElements() {
                        return !hasGetted;
                    }
                };
            }
            return super.getHeaders(name);
        }

        @Override
        public String getHeader(String name) {
            String headerValue = customHeaders.get(name);
            if (null != name && name.equalsIgnoreCase(CommonConstant.JWT_HEADER_TOKEN_KEY)) {
                return getRedisAuthorizationToken();
            } else {
                return ValidatorUtil.notEmpty(headerValue) ? headerValue : super.getHeader(name);
            }
        }

        private String getRedisAuthorizationToken() {
            return authorizationToken;
        }
    }
}
